home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 151 / cd-rom 151.iso / internet / firefox / Firefox Setup 3.0 Beta 1.exe / nonlocalized / components / fuelApplication.js < prev    next >
Encoding:
JavaScript  |  2007-11-09  |  33.8 KB  |  1,318 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is FUEL.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla Corporation.
  17.  * Portions created by the Initial Developer are Copyright (C) 2006
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Mark Finkle <mfinkle@mozilla.com> (Original Author)
  22.  *  John Resig  <jresig@mozilla.com> (Original Author)
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const Ci = Components.interfaces;
  39. const Cc = Components.classes;
  40.  
  41. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  42.  
  43. //=================================================
  44. // Shutdown - used to store cleanup functions which will
  45. //            be called on Application shutdown
  46. var gShutdown = [];
  47.  
  48. //=================================================
  49. // Console constructor
  50. function Console() {
  51.   this._console = Components.classes["@mozilla.org/consoleservice;1"]
  52.     .getService(Ci.nsIConsoleService);
  53. }
  54.  
  55. //=================================================
  56. // Console implementation
  57. Console.prototype = {
  58.   log : function cs_log(aMsg) {
  59.     this._console.logStringMessage(aMsg);
  60.   },
  61.  
  62.   open : function cs_open() {
  63.     var wMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  64.                               .getService(Ci.nsIWindowMediator);
  65.     var console = wMediator.getMostRecentWindow("global:console");
  66.     if (!console) {
  67.       var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  68.                              .getService(Ci.nsIWindowWatcher);
  69.       wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
  70.                         "chrome,dialog=no,all", null);
  71.     } else {
  72.       // console was already open
  73.       console.focus();
  74.     }
  75.   },
  76.  
  77.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIConsole])
  78. };
  79.  
  80.  
  81. //=================================================
  82. // EventItem constructor
  83. function EventItem(aType, aData) {
  84.   this._type = aType;
  85.   this._data = aData;
  86. }
  87.  
  88. //=================================================
  89. // EventItem implementation
  90. EventItem.prototype = {
  91.   _cancel : false,
  92.  
  93.   get type() {
  94.     return this._type;
  95.   },
  96.  
  97.   get data() {
  98.     return this._data;
  99.   },
  100.  
  101.   preventDefault : function ei_pd() {
  102.     this._cancel = true;
  103.   },
  104.  
  105.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIEventItem])
  106. };
  107.  
  108.  
  109. //=================================================
  110. // Events constructor
  111. function Events() {
  112.   this._listeners = [];
  113. }
  114.  
  115. //=================================================
  116. // Events implementation
  117. Events.prototype = {
  118.   addListener : function evts_al(aEvent, aListener) {
  119.     if (this._listeners.some(hasFilter))
  120.       return;
  121.  
  122.     this._listeners.push({
  123.       event: aEvent,
  124.       listener: aListener
  125.     });
  126.  
  127.     function hasFilter(element) {
  128.       return element.event == aEvent && element.listener == aListener;
  129.     }
  130.   },
  131.  
  132.   removeListener : function evts_rl(aEvent, aListener) {
  133.     this._listeners = this._listeners.filter(function(element){
  134.       return element.event != aEvent && element.listener != aListener;
  135.     });
  136.   },
  137.  
  138.   dispatch : function evts_dispatch(aEvent, aEventItem) {
  139.     eventItem = new EventItem(aEvent, aEventItem);
  140.  
  141.     this._listeners.forEach(function(key){
  142.       if (key.event == aEvent) {
  143.         key.listener.handleEvent ?
  144.           key.listener.handleEvent(eventItem) :
  145.           key.listener(eventItem);
  146.       }
  147.     });
  148.  
  149.     return !eventItem._cancel;
  150.   },
  151.  
  152.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIEvents])
  153. };
  154.  
  155.  
  156. //=================================================
  157. // PreferenceBranch constructor
  158. function PreferenceBranch(aBranch) {
  159.   if (!aBranch)
  160.     aBranch = "";
  161.  
  162.   this._root = aBranch;
  163.   this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
  164.                           .getService(Ci.nsIPrefService);
  165.  
  166.   if (aBranch)
  167.     this._prefs = this._prefs.getBranch(aBranch);
  168.  
  169.   this._prefs.QueryInterface(Ci.nsIPrefBranch);
  170.   this._prefs.QueryInterface(Ci.nsIPrefBranch2);
  171.  
  172.   // we want to listen to "all" changes for this branch, so pass in a blank domain
  173.   this._prefs.addObserver("", this, true);
  174.   this._events = new Events();
  175.  
  176.   var self = this;
  177.   gShutdown.push(function() { self._shutdown(); });
  178. }
  179.  
  180. //=================================================
  181. // PreferenceBranch implementation
  182. PreferenceBranch.prototype = {
  183.   // cleanup observer so we don't leak
  184.   _shutdown: function prefs_shutdown() {
  185.     this._prefs.removeObserver(this._root, this);
  186.  
  187.     this._prefs = null;
  188.     this._events = null;
  189.   },
  190.  
  191.   // for nsIObserver
  192.   observe: function prefs_observe(aSubject, aTopic, aData) {
  193.     if (aTopic == "nsPref:changed")
  194.       this._events.dispatch("change", aData);
  195.   },
  196.  
  197.   get root() {
  198.     return this._root;
  199.   },
  200.  
  201.   get all() {
  202.     return this.find({});
  203.   },
  204.  
  205.   get events() {
  206.     return this._events;
  207.   },
  208.  
  209.   // XXX: Disabled until we can figure out the wrapped object issues
  210.   // name: "name" or /name/
  211.   // path: "foo.bar." or "" or /fo+\.bar/
  212.   // type: Boolean, Number, String (getPrefType)
  213.   // locked: true, false (prefIsLocked)
  214.   // modified: true, false (prefHasUserValue)
  215.   find : function prefs_find(aOptions) {
  216.     var retVal = [];
  217.     var items = this._prefs.getChildList("", []);
  218.  
  219.     for (var i = 0; i < items.length; i++) {
  220.       retVal.push(new Preference(items[i], this));
  221.     }
  222.  
  223.     return retVal;
  224.   },
  225.  
  226.   has : function prefs_has(aName) {
  227.     return (this._prefs.getPrefType(aName) != Ci.nsIPrefBranch.PREF_INVALID);
  228.   },
  229.  
  230.   get : function prefs_get(aName) {
  231.     return this.has(aName) ? new Preference(aName, this) : null;
  232.   },
  233.  
  234.   getValue : function prefs_gv(aName, aValue) {
  235.     var type = this._prefs.getPrefType(aName);
  236.  
  237.     switch (type) {
  238.       case Ci.nsIPrefBranch2.PREF_STRING:
  239.         aValue = this._prefs.getComplexValue(aName, Ci.nsISupportsString).data;
  240.         break;
  241.       case Ci.nsIPrefBranch2.PREF_BOOL:
  242.         aValue = this._prefs.getBoolPref(aName);
  243.         break;
  244.       case Ci.nsIPrefBranch2.PREF_INT:
  245.         aValue = this._prefs.getIntPref(aName);
  246.         break;
  247.     }
  248.  
  249.     return aValue;
  250.   },
  251.  
  252.   setValue : function prefs_sv(aName, aValue) {
  253.     var type = aValue != null ? aValue.constructor.name : "";
  254.  
  255.     switch (type) {
  256.       case "String":
  257.         var str = Components.classes["@mozilla.org/supports-string;1"]
  258.                             .createInstance(Ci.nsISupportsString);
  259.         str.data = aValue;
  260.         this._prefs.setComplexValue(aName, Ci.nsISupportsString, str);
  261.         break;
  262.       case "Boolean":
  263.         this._prefs.setBoolPref(aName, aValue);
  264.         break;
  265.       case "Number":
  266.         this._prefs.setIntPref(aName, aValue);
  267.         break;
  268.       default:
  269.         throw("Unknown preference value specified.");
  270.     }
  271.   },
  272.  
  273.   reset : function prefs_reset() {
  274.     this._prefs.resetBranch("");
  275.   },
  276.  
  277.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIPreferenceBranch, Ci.nsISupportsWeakReference])
  278. };
  279.  
  280.  
  281. //=================================================
  282. // Preference constructor
  283. function Preference(aName, aBranch) {
  284.   this._name = aName;
  285.   this._branch = aBranch;
  286.   this._events = new Events();
  287.  
  288.   var self = this;
  289.  
  290.   this.branch.events.addListener("change", function(aEvent){
  291.     if (aEvent.data == self.name)
  292.       self.events.dispatch(aEvent.type, aEvent.data);
  293.   });
  294. }
  295.  
  296. //=================================================
  297. // Preference implementation
  298. Preference.prototype = {
  299.   get name() {
  300.     return this._name;
  301.   },
  302.  
  303.   get type() {
  304.     var value = "";
  305.     var type = this._prefs.getPrefType(name);
  306.  
  307.     switch (type) {
  308.       case Ci.nsIPrefBranch2.PREF_STRING:
  309.         value = "String";
  310.         break;
  311.       case Ci.nsIPrefBranch2.PREF_BOOL:
  312.         value = "Boolean";
  313.         break;
  314.       case Ci.nsIPrefBranch2.PREF_INT:
  315.         value = "Number";
  316.         break;
  317.     }
  318.  
  319.     return value;
  320.   },
  321.  
  322.   get value() {
  323.     return this.branch.getValue(this._name, null);
  324.   },
  325.  
  326.   set value(aValue) {
  327.     return this.branch.setValue(this._name, aValue);
  328.   },
  329.  
  330.   get locked() {
  331.     return this.branch._prefs.prefIsLocked(this.name);
  332.   },
  333.  
  334.   set locked(aValue) {
  335.     this.branch._prefs[ aValue ? "lockPref" : "unlockPref" ](this.name);
  336.   },
  337.  
  338.   get modified() {
  339.     return this.branch._prefs.prefHasUserValue(this.name);
  340.   },
  341.  
  342.   get branch() {
  343.     return this._branch;
  344.   },
  345.  
  346.   get events() {
  347.     return this._events;
  348.   },
  349.  
  350.   reset : function pref_reset() {
  351.     this.branch._prefs.clearUserPref(this.name);
  352.   },
  353.  
  354.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIPreference])
  355. };
  356.  
  357.  
  358. //=================================================
  359. // SessionStorage constructor
  360. function SessionStorage() {
  361.   this._storage = {};
  362.   this._events = new Events();
  363. }
  364.  
  365. //=================================================
  366. // SessionStorage implementation
  367. SessionStorage.prototype = {
  368.   get events() {
  369.     return this._events;
  370.   },
  371.  
  372.   has : function ss_has(aName) {
  373.     return this._storage.hasOwnProperty(aName);
  374.   },
  375.  
  376.   set : function ss_set(aName, aValue) {
  377.     this._storage[aName] = aValue;
  378.     this._events.dispatch("change", aName);
  379.   },
  380.  
  381.   get : function ss_get(aName, aDefaultValue) {
  382.     return this.has(aName) ? this._storage[aName] : aDefaultValue;
  383.   },
  384.  
  385.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelISessionStorage])
  386. };
  387.  
  388.  
  389. //=================================================
  390. // Extension constructor
  391. function Extension(aItem) {
  392.   this._item = aItem;
  393.   this._firstRun = false;
  394.   this._prefs = new PreferenceBranch("extensions." + this._item.id + ".");
  395.   this._storage = new SessionStorage();
  396.   this._events = new Events();
  397.  
  398.   var installPref = "install-event-fired";
  399.   if (!this._prefs.has(installPref)) {
  400.     this._prefs.setValue(installPref, true);
  401.     this._firstRun = true;
  402.   }
  403.  
  404.   this._enabled = false;
  405.   const PREFIX_ITEM_URI = "urn:mozilla:item:";
  406.   const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
  407.   var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
  408.   var itemResource = rdf.GetResource(PREFIX_ITEM_URI + this._item.id);
  409.   if (itemResource) {
  410.     var extmgr = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
  411.     var ds = extmgr.datasource;
  412.     var target = ds.GetTarget(itemResource, rdf.GetResource(PREFIX_NS_EM + "isDisabled"), true);
  413.     if (target && target instanceof Ci.nsIRDFLiteral)
  414.       this._enabled = (target.Value != "true");
  415.   }
  416.  
  417.   var os = Components.classes["@mozilla.org/observer-service;1"]
  418.                      .getService(Ci.nsIObserverService);
  419.   os.addObserver(this, "em-action-requested", false);
  420.  
  421.   var self = this;
  422.   gShutdown.push(function(){ self._shutdown(); });
  423. }
  424.  
  425. //=================================================
  426. // Extension implementation
  427. Extension.prototype = {
  428.   // cleanup observer so we don't leak
  429.   _shutdown: function ext_shutdown() {
  430.     var os = Components.classes["@mozilla.org/observer-service;1"]
  431.                        .getService(Ci.nsIObserverService);
  432.     os.removeObserver(this, "em-action-requested");
  433.  
  434.     this._prefs = null;
  435.     this._storage = null;
  436.     this._events = null;
  437.   },
  438.  
  439.   // for nsIObserver
  440.   observe: function ext_observe(aSubject, aTopic, aData)
  441.   {
  442.     if ((aSubject instanceof Ci.nsIUpdateItem) && (aSubject.id == this._item.id))
  443.     {
  444.       if (aData == "item-uninstalled")
  445.         this._events.dispatch("uninstall", this._item.id);
  446.       else if (aData == "item-disabled")
  447.         this._events.dispatch("disable", this._item.id);
  448.       else if (aData == "item-enabled")
  449.         this._events.dispatch("enable", this._item.id);
  450.       else if (aData == "item-cancel-action")
  451.         this._events.dispatch("cancel", this._item.id);
  452.       else if (aData == "item-upgraded")
  453.         this._events.dispatch("upgrade", this._item.id);
  454.     }
  455.   },
  456.  
  457.   get id() {
  458.     return this._item.id;
  459.   },
  460.  
  461.   get name() {
  462.     return this._item.name;
  463.   },
  464.  
  465.   get enabled() {
  466.     return this._enabled;
  467.   },
  468.  
  469.   get version() {
  470.     return this._item.version;
  471.   },
  472.  
  473.   get firstRun() {
  474.     return this._firstRun;
  475.   },
  476.  
  477.   get storage() {
  478.     return this._storage;
  479.   },
  480.  
  481.   get prefs() {
  482.     return this._prefs;
  483.   },
  484.  
  485.   get events() {
  486.     return this._events;
  487.   },
  488.  
  489.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIExtension])
  490. };
  491.  
  492.  
  493. //=================================================
  494. // Extensions constructor
  495. function Extensions() {
  496.   this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
  497.                            .getService(Ci.nsIExtensionManager);
  498.  
  499.   this._cache = {};
  500.  
  501.   var self = this;
  502.   gShutdown.push(function() { self._shutdown(); });
  503. }
  504.  
  505. //=================================================
  506. // Extensions implementation
  507. Extensions.prototype = {
  508.   _shutdown : function exts_shutdown() {
  509.     this._extmgr = null;
  510.     this._cache = null;
  511.   },
  512.  
  513.   /*
  514.    * Helper method to check cache before creating a new extension
  515.    */
  516.   _get : function exts_get(aId) {
  517.     if (this._cache.hasOwnProperty(aId))
  518.       return this._cache[aId];
  519.  
  520.     var newExt = new Extension(this._extmgr.getItemForID(aId));
  521.     this._cache[aId] = newExt;
  522.     return newExt;
  523.   },
  524.  
  525.   get all() {
  526.     return this.find({});
  527.   },
  528.  
  529.   // XXX: Disabled until we can figure out the wrapped object issues
  530.   // id: "some@id" or /id/
  531.   // name: "name" or /name/
  532.   // version: "1.0.1"
  533.   // minVersion: "1.0"
  534.   // maxVersion: "2.0"
  535.   find : function exts_find(aOptions) {
  536.     var retVal = [];
  537.     var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
  538.  
  539.     for (var i = 0; i < items.length; i++) {
  540.       retVal.push(this._get(items[i].id));
  541.     }
  542.  
  543.     return retVal;
  544.   },
  545.  
  546.   has : function exts_has(aId) {
  547.     return this._extmgr.getItemForID(aId) != null;
  548.   },
  549.  
  550.   get : function exts_get(aId) {
  551.     return this.has(aId) ? this._get(aId) : null;
  552.   },
  553.  
  554.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIExtensions])
  555. };
  556.  
  557. //=================================================
  558. // Singleton that holds services and utilities
  559. var Utilities = {
  560.   _bookmarks : null,
  561.   get bookmarks() {
  562.     if (!this._bookmarks) {
  563.       this._bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
  564.                         getService(Ci.nsINavBookmarksService);
  565.     }
  566.     return this._bookmarks;
  567.   },
  568.  
  569.   _livemarks : null,
  570.   get livemarks() {
  571.     if (!this._livemarks) {
  572.       this._livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
  573.                         getService(Ci.nsILivemarkService);
  574.     }
  575.     return this._livemarks;
  576.   },
  577.  
  578.   _annotations : null,
  579.   get annotations() {
  580.     if (!this._annotations) {
  581.       this._annotations = Cc["@mozilla.org/browser/annotation-service;1"].
  582.                           getService(Ci.nsIAnnotationService);
  583.     }
  584.     return this._annotations;
  585.   },
  586.  
  587.   _history : null,
  588.   get history() {
  589.     if (!this._history) {
  590.       this._history = Cc["@mozilla.org/browser/nav-history-service;1"].
  591.                       getService(Ci.nsINavHistoryService);
  592.     }
  593.     return this._history;
  594.   },
  595.  
  596.   _windowMediator : null,
  597.   get windowMediator() {
  598.     if (!this._windowMediator) {
  599.       this._windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
  600.                              getService(Ci.nsIWindowMediator);
  601.     }
  602.     return this._windowMediator;
  603.   },
  604.  
  605.   makeURI : function(aSpec) {
  606.     if (!aSpec)
  607.       return null;
  608.     var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  609.     return ios.newURI(aSpec, null, null);
  610.   },
  611.  
  612.   free : function() {
  613.     this._bookmarks = null;
  614.     this._livemarks = null;
  615.     this._annotations = null;
  616.     this._history = null;
  617.     this._windowMediator = null;
  618.   }
  619. };
  620.  
  621.  
  622. //=================================================
  623. // Window implementation
  624. function Window(aWindow) {
  625.   this._window = aWindow;
  626.   this._tabbrowser = aWindow.getBrowser();
  627.   this._events = new Events();
  628.   this._cleanup = {};
  629.  
  630.   this._watch("TabOpen");
  631.   this._watch("TabMove");
  632.   this._watch("TabClose");
  633.   this._watch("TabSelect");
  634.  
  635.   var self = this;
  636.   gShutdown.push(function() { self._shutdown(); });
  637. }
  638.  
  639. Window.prototype = {
  640.   get events() {
  641.     return this._events;
  642.   },
  643.  
  644.   /*
  645.    * Helper used to setup event handlers on the XBL element. Note that the events
  646.    * are actually dispatched to tabs, so we capture them.
  647.    */
  648.   _watch : function win_watch(aType) {
  649.     var self = this;
  650.     this._tabbrowser.addEventListener(aType,
  651.       this._cleanup[aType] = function(e){ self._event(e); },
  652.       true);
  653.   },
  654.  
  655.   /*
  656.    * Helper event callback used to redirect events made on the XBL element
  657.    */
  658.   _event : function win_event(aEvent) {
  659.     this._events.dispatch(aEvent.type, "");
  660.   },
  661.  
  662.   get tabs() {
  663.     var tabs = [];
  664.     var browsers = this._tabbrowser.browsers;
  665.  
  666.     for (var i=0; i<browsers.length; i++)
  667.       tabs.push(new BrowserTab(this._window, browsers[i]));
  668.  
  669.     return tabs;
  670.   },
  671.  
  672.   get activeTab() {
  673.     return new BrowserTab(this._window, this._tabbrowser.selectedBrowser);
  674.   },
  675.  
  676.   open : function win_open(aURI) {
  677.     return new BrowserTab(this._window, this._tabbrowser.addTab(aURI.spec).linkedBrowser);
  678.   },
  679.  
  680.   _shutdown : function win_shutdown() {
  681.     for (var type in this._cleanup)
  682.       this._tabbrowser.removeEventListener(type, this._cleanup[type], true);
  683.     this._cleanup = null;
  684.  
  685.     this._window = null;
  686.     this._tabbrowser = null;
  687.     this._events = null;
  688.   },
  689.  
  690.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIWindow])
  691. };
  692.  
  693.  
  694. //=================================================
  695. // BrowserTab implementation
  696. function BrowserTab(aWindow, aBrowser) {
  697.   this._window = aWindow;
  698.   this._tabbrowser = aWindow.getBrowser();
  699.   this._browser = aBrowser;
  700.   this._events = new Events();
  701.   this._cleanup = {};
  702.  
  703.   this._watch("load");
  704.  
  705.   var self = this;
  706.   gShutdown.push(function() { self._shutdown(); });
  707. }
  708.  
  709. BrowserTab.prototype = {
  710.   get uri() {
  711.     return this._browser.currentURI;
  712.   },
  713.  
  714.   get index() {
  715.     var tabs = this._tabbrowser.mTabs;
  716.     for (var i=0; i<tabs.length; i++) {
  717.       if (tabs[i].linkedBrowser == this._browser)
  718.         return i;
  719.     }
  720.     return -1;
  721.   },
  722.  
  723.   get events() {
  724.     return this._events;
  725.   },
  726.  
  727.   get window() {
  728.     return this._window;
  729.   },
  730.  
  731.   get document() {
  732.     return this._browser.contentDocument;
  733.   },
  734.  
  735.   /*
  736.    * Helper used to setup event handlers on the XBL element
  737.    */
  738.   _watch : function bt_watch(aType) {
  739.     var self = this;
  740.     this._browser.addEventListener(aType,
  741.       this._cleanup[aType] = function(e){ self._event(e); },
  742.       true);
  743.   },
  744.  
  745.   /*
  746.    * Helper event callback used to redirect events made on the XBL element
  747.    */
  748.   _event : function bt_event(aEvent) {
  749.     if (aEvent.type == "load") {
  750.       if (!(aEvent.originalTarget instanceof Ci.nsIDOMHTMLDocument))
  751.         return;
  752.  
  753.       if (aEvent.originalTarget.defaultView instanceof Ci.nsIDOMWindowInternal &&
  754.           aEvent.originalTarget.defaultView.frameElement)
  755.         return;
  756.     }
  757.  
  758.     this._events.dispatch(aEvent.type, "");
  759.   },
  760.  
  761.   /*
  762.    * Helper used to determine the index offset of the browsertab
  763.    */
  764.   _getTab : function bt_gettab() {
  765.     var tabs = this._tabbrowser.mTabs;
  766.     return tabs[this.index] || null;
  767.   },
  768.  
  769.   load : function bt_load(aURI) {
  770.     this._browser.loadURI(aURI.spec, null, null);
  771.   },
  772.  
  773.   focus : function bt_focus() {
  774.     this._tabbrowser.selectedTab = this._getTab();
  775.     this._tabbrowser.focus();
  776.   },
  777.  
  778.   close : function bt_close() {
  779.     this._tabbrowser.removeTab(this._getTab());
  780.   },
  781.  
  782.   moveBefore : function bt_movebefore(aBefore) {
  783.     this._tabbrowser.moveTabTo(this._getTab(), aBefore.index);
  784.   },
  785.  
  786.   moveToEnd : function bt_moveend() {
  787.     this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length);
  788.   },
  789.  
  790.   _shutdown : function bt_shutdown() {
  791.     for (var type in this._cleanup)
  792.       this._browser.removeEventListener(type, this._cleanup[type], true);
  793.     this._cleanup = null;
  794.  
  795.     this._window = null;
  796.     this._tabbrowser = null;
  797.     this._browser = null;
  798.     this._events = null;
  799.   },
  800.  
  801.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBrowserTab])
  802. };
  803.  
  804.  
  805. //=================================================
  806. // Annotations implementation
  807. function Annotations(aId) {
  808.   this._id = aId;
  809. }
  810.  
  811. Annotations.prototype = {
  812.   get names() {
  813.     return Utilities.annotations.getItemAnnotationNames(this._id, {});
  814.   },
  815.  
  816.   has : function ann_has(aName) {
  817.     return Utilities.annotations.itemHasAnnotation(this._id, aName);
  818.   },
  819.  
  820.   get : function(aName) {
  821.     if (this.has(aName))
  822.       return Utilities.annotations.getItemAnnotation(this._id, aName);
  823.     return null;
  824.   },
  825.  
  826.   set : function(aName, aValue, aExpiration) {
  827.     Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration);
  828.   },
  829.  
  830.   remove : function ann_remove(aName) {
  831.     if (aName)
  832.       Utilities.annotations.removeItemAnnotation(this._id, aName);
  833.   },
  834.  
  835.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIAnnotations])
  836. };
  837.  
  838.  
  839. //=================================================
  840. // Bookmark implementation
  841. function Bookmark(aId, aParent, aType) {
  842.   this._id = aId;
  843.   this._parent = aParent;
  844.   this._type = aType || "bookmark";
  845.   this._annotations = new Annotations(this._id);
  846.   this._events = new Events();
  847.  
  848.   Utilities.bookmarks.addObserver(this, false);
  849.  
  850.   var self = this;
  851.   gShutdown.push(function() { self._shutdown(); });
  852. }
  853.  
  854. Bookmark.prototype = {
  855.   _shutdown : function bm_shutdown() {
  856.     this._annotations = null;
  857.     this._events = null;
  858.  
  859.     Utilities.bookmarks.removeObserver(this);
  860.   },
  861.  
  862.   get id() {
  863.     return this._id;
  864.   },
  865.  
  866.   get title() {
  867.     return Utilities.bookmarks.getItemTitle(this._id);
  868.   },
  869.  
  870.   set title(aTitle) {
  871.     Utilities.bookmarks.setItemTitle(this._id, aTitle);
  872.   },
  873.  
  874.   get uri() {
  875.     return Utilities.bookmarks.getBookmarkURI(this._id);
  876.   },
  877.  
  878.   set uri(aURI) {
  879.     return Utilities.bookmarks.changeBookmarkURI(this._id, aURI);
  880.   },
  881.  
  882.   get description() {
  883.     return this._annotations.get("bookmarkProperties/description");
  884.   },
  885.  
  886.   set description(aDesc) {
  887.     this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
  888.   },
  889.  
  890.   get keyword() {
  891.     return Utilities.bookmarks.getKeywordForBookmark(this._id);
  892.   },
  893.  
  894.   set keyword(aKeyword) {
  895.     Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword);
  896.   },
  897.  
  898.   get type() {
  899.     return this._type;
  900.   },
  901.  
  902.   get parent() {
  903.     return this._parent;
  904.   },
  905.  
  906.   set parent(aFolder) {
  907.     Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
  908.     // this._parent is updated in onItemMoved
  909.   },
  910.  
  911.   get annotations() {
  912.     return this._annotations;
  913.   },
  914.  
  915.   get events() {
  916.     return this._events;
  917.   },
  918.  
  919.   remove : function bm_remove() {
  920.     Utilities.bookmarks.removeItem(this._id);
  921.   },
  922.  
  923.   // observer
  924.   onBeginUpdateBatch : function bm_obub() {
  925.   },
  926.  
  927.   onEndUpdateBatch : function bm_oeub() {
  928.   },
  929.  
  930.   onItemAdded : function bm_oia(aId, aFolder, aIndex) {
  931.     // bookmark object doesn't exist at this point
  932.   },
  933.  
  934.   onItemRemoved : function bm_oir(aId, aFolder, aIndex) {
  935.     if (this._id == aId)
  936.       this._events.dispatch("remove", aId);
  937.   },
  938.  
  939.   onItemChanged : function bm_oic(aId, aProperty, aIsAnnotationProperty, aValue) {
  940.     if (this._id == aId)
  941.       this._events.dispatch("change", aProperty);
  942.   },
  943.  
  944.   onItemVisited: function bm_oiv(aId, aVisitID, aTime) {
  945.   },
  946.  
  947.   onItemMoved: function bm_oim(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
  948.     if (this._id == aId) {
  949.       this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
  950.       this._events.dispatch("move", aId);
  951.     }
  952.   },
  953.  
  954.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmark, Ci.nsINavBookmarkObserver])
  955. };
  956.  
  957.  
  958. //=================================================
  959. // BookmarkFolder implementation
  960. function BookmarkFolder(aId, aParent) {
  961.   this._id = aId;
  962.   if (this._id == null)
  963.     this._id = Utilities.bookmarks.bookmarksRoot;
  964.  
  965.   this._parent = aParent;
  966.  
  967.   this._annotations = new Annotations(this._id);
  968.   this._events = new Events();
  969.  
  970.   Utilities.bookmarks.addObserver(this, false);
  971.  
  972.   var self = this;
  973.   gShutdown.push(function() { self._shutdown(); });
  974. }
  975.  
  976. BookmarkFolder.prototype = {
  977.   _shutdown : function bmf_shutdown() {
  978.     this._annotations = null;
  979.     this._events = null;
  980.  
  981.     Utilities.bookmarks.removeObserver(this);
  982.   },
  983.  
  984.   get id() {
  985.     return this._id;
  986.   },
  987.  
  988.   get title() {
  989.     return Utilities.bookmarks.getItemTitle(this._id);
  990.   },
  991.  
  992.   set title(aTitle) {
  993.     Utilities.bookmarks.setItemTitle(this._id, aTitle);
  994.   },
  995.  
  996.   get description() {
  997.     return this._annotations.get("bookmarkProperties/description");
  998.   },
  999.  
  1000.   set description(aDesc) {
  1001.     this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
  1002.   },
  1003.  
  1004.   get type() {
  1005.     return "folder";
  1006.   },
  1007.  
  1008.   get parent() {
  1009.     return this._parent;
  1010.   },
  1011.  
  1012.   set parent(aFolder) {
  1013.     Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
  1014.     // this._parent is updated in onItemMoved
  1015.   },
  1016.  
  1017.   get annotations() {
  1018.     return this._annotations;
  1019.   },
  1020.  
  1021.   get events() {
  1022.     return this._events;
  1023.   },
  1024.  
  1025.   get children() {
  1026.     var items = [];
  1027.  
  1028.     var options = Utilities.history.getNewQueryOptions();
  1029.     var query = Utilities.history.getNewQuery();
  1030.     query.setFolders([this._id], 1);
  1031.     var result = Utilities.history.executeQuery(query, options);
  1032.     var rootNode = result.root;
  1033.     rootNode.containerOpen = true;
  1034.     var cc = rootNode.childCount;
  1035.     for (var i=0; i<cc; ++i) {
  1036.       var node = rootNode.getChild(i);
  1037.       if (node.type == node.RESULT_TYPE_FOLDER) {
  1038.         var folder = new BookmarkFolder(node.itemId, this._id);
  1039.         items.push(folder);
  1040.       }
  1041.       else if (node.type == node.RESULT_TYPE_SEPARATOR) {
  1042.         var separator = new Bookmark(node.itemId, this._id, "separator");
  1043.         items.push(separator);
  1044.       }
  1045.       else {
  1046.         var bookmark = new Bookmark(node.itemId, this._id, "bookmark");
  1047.         items.push(bookmark);
  1048.       }
  1049.     }
  1050.     rootNode.containerOpen = false;
  1051.  
  1052.     return items;
  1053.   },
  1054.  
  1055.   addBookmark : function bmf_addbm(aTitle, aUri) {
  1056.     var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle);
  1057.     var newBookmark = new Bookmark(newBookmarkID, this, "bookmark");
  1058.     return newBookmark;
  1059.   },
  1060.  
  1061.   addSeparator : function bmf_addsep() {
  1062.     var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX);
  1063.     var newBookmark = new Bookmark(newBookmarkID, this, "separator");
  1064.     return newBookmark;
  1065.   },
  1066.  
  1067.   addFolder : function bmf_addfolder(aTitle) {
  1068.     var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX);
  1069.     var newFolder = new BookmarkFolder(newFolderID, this);
  1070.     return newFolder;
  1071.   },
  1072.  
  1073.   remove : function bmf_remove() {
  1074.     Utilities.bookmarks.removeFolder(this._id);
  1075.   },
  1076.  
  1077.   // observer
  1078.   onBeginUpdateBatch : function bmf_obub() {
  1079.   },
  1080.  
  1081.   onEndUpdateBatch : function bmf_oeub() {
  1082.   },
  1083.  
  1084.   onItemAdded : function bmf_oia(aId, aFolder, aIndex) {
  1085.     // handle root folder events
  1086.     if (!this._parent)
  1087.       this._events.dispatch("add", aId);
  1088.  
  1089.     // handle this folder events
  1090.     if (this._id == aFolder)
  1091.       this._events.dispatch("addchild", aId);
  1092.   },
  1093.  
  1094.   onItemRemoved : function bmf_oir(aId, aFolder, aIndex) {
  1095.     // handle root folder events
  1096.     if (!this._parent || this._id == aId)
  1097.       this._events.dispatch("remove", aId);
  1098.  
  1099.     // handle this folder events
  1100.     if (this._id == aFolder)
  1101.       this._events.dispatch("removechild", aId);
  1102.   },
  1103.  
  1104.   onItemChanged : function bmf_oic(aId, aProperty, aIsAnnotationProperty, aValue) {
  1105.     // handle root folder and this folder events
  1106.     if (!this._parent || this._id == aId)
  1107.       this._events.dispatch("change", aProperty);
  1108.   },
  1109.  
  1110.   onItemVisited: function bmf_oiv(aId, aVisitID, aTime) {
  1111.   },
  1112.  
  1113.   onItemMoved: function bmf_oim(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
  1114.     // handle this folder event, root folder cannot be moved
  1115.     if (this._id == aId) {
  1116.       this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
  1117.       this._events.dispatch("move", aId);
  1118.     }
  1119.   },
  1120.  
  1121.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder, Ci.nsINavBookmarkObserver])
  1122. };
  1123.  
  1124. //=================================================
  1125. // Factory - Treat Application as a singleton
  1126. // XXX This is required, because we're registered for the 'JavaScript global
  1127. // privileged property' category, whose handler always calls createInstance.
  1128. // See bug 386535.
  1129. var gSingleton = null;
  1130. var ApplicationFactory = {
  1131.   createInstance: function af_ci(aOuter, aIID) {
  1132.     if (aOuter != null)
  1133.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  1134.  
  1135.     if (gSingleton == null) {
  1136.       gSingleton = new Application();
  1137.     }
  1138.  
  1139.     return gSingleton.QueryInterface(aIID);
  1140.   }
  1141. };
  1142.  
  1143. //=================================================
  1144. // Application constructor
  1145. function Application() {
  1146.   this._console = null;
  1147.   this._prefs = null;
  1148.   this._storage = null;
  1149.   this._events = null;
  1150.   this._bookmarks = null;
  1151.  
  1152.   this._info = Components.classes["@mozilla.org/xre/app-info;1"]
  1153.                      .getService(Ci.nsIXULAppInfo);
  1154.  
  1155.   var os = Components.classes["@mozilla.org/observer-service;1"]
  1156.                      .getService(Ci.nsIObserverService);
  1157.  
  1158.   os.addObserver(this, "final-ui-startup", false);
  1159.   os.addObserver(this, "quit-application-requested", false);
  1160.   os.addObserver(this, "quit-application-granted", false);
  1161.   os.addObserver(this, "quit-application", false);
  1162.   os.addObserver(this, "xpcom-shutdown", false);
  1163. }
  1164.  
  1165. //=================================================
  1166. // Application implementation
  1167. Application.prototype = {
  1168.   // for nsIClassInfo + XPCOMUtils
  1169.   classDescription: "Application",
  1170.   classID:          Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"),
  1171.   contractID:       "@mozilla.org/fuel/application;1",
  1172.  
  1173.   // redefine the default factory for XPCOMUtils
  1174.   _xpcom_factory: ApplicationFactory,
  1175.  
  1176.   // get this contractID registered for certain categories via XPCOMUtils
  1177.   _xpcom_categories: [
  1178.     // make Application a startup observer
  1179.     { category: "app-startup", service: true },
  1180.  
  1181.     // add Application as a global property for easy access
  1182.     { category: "JavaScript global privileged property" }
  1183.   ],
  1184.  
  1185.   get id() {
  1186.     return this._info.ID;
  1187.   },
  1188.  
  1189.   get name() {
  1190.     return this._info.name;
  1191.   },
  1192.  
  1193.   get version() {
  1194.     return this._info.version;
  1195.   },
  1196.  
  1197.   // for nsIObserver
  1198.   observe: function app_observe(aSubject, aTopic, aData) {
  1199.     if (aTopic == "app-startup") {
  1200.       this._extensions = new Extensions();
  1201.       this.events.dispatch("load", "application");
  1202.     }
  1203.     else if (aTopic == "final-ui-startup") {
  1204.       this.events.dispatch("ready", "application");
  1205.     }
  1206.     else if (aTopic == "quit-application-requested") {
  1207.       // we can stop the quit by checking the return value
  1208.       if (this.events.dispatch("quit", "application") == false)
  1209.         aSubject.data = true;
  1210.     }
  1211.     else if (aTopic == "xpcom-shutdown") {
  1212.       this.events.dispatch("unload", "application");
  1213.  
  1214.       // call the cleanup functions and empty the array
  1215.       while (gShutdown.length) {
  1216.         gShutdown.shift()();
  1217.       }
  1218.  
  1219.       // release our observers
  1220.       var os = Components.classes["@mozilla.org/observer-service;1"]
  1221.                          .getService(Ci.nsIObserverService);
  1222.  
  1223.       os.removeObserver(this, "final-ui-startup");
  1224.  
  1225.       os.removeObserver(this, "quit-application-requested");
  1226.       os.removeObserver(this, "quit-application-granted");
  1227.       os.removeObserver(this, "quit-application");
  1228.  
  1229.       os.removeObserver(this, "xpcom-shutdown");
  1230.  
  1231.       this._info = null;
  1232.       this._console = null;
  1233.       this._prefs = null;
  1234.       this._storage = null;
  1235.       this._events = null;
  1236.       this._extensions = null;
  1237.       this._bookmarks = null;
  1238.  
  1239.       Utilities.free();
  1240.     }
  1241.   },
  1242.  
  1243.   // for nsIClassInfo
  1244.   flags : Ci.nsIClassInfo.SINGLETON,
  1245.   implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
  1246.  
  1247.   getInterfaces : function app_gi(aCount) {
  1248.     var interfaces = [Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo];
  1249.     aCount.value = interfaces.length;
  1250.     return interfaces;
  1251.   },
  1252.  
  1253.   getHelperForLanguage : function app_ghfl(aCount) {
  1254.     return null;
  1255.   },
  1256.  
  1257.   // for nsISupports
  1258.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo]),
  1259.  
  1260.   get console() {
  1261.     if (this._console == null)
  1262.         this._console = new Console();
  1263.  
  1264.     return this._console;
  1265.   },
  1266.  
  1267.   get storage() {
  1268.     if (this._storage == null)
  1269.         this._storage = new SessionStorage();
  1270.  
  1271.     return this._storage;
  1272.   },
  1273.  
  1274.   get prefs() {
  1275.     if (this._prefs == null)
  1276.         this._prefs = new PreferenceBranch("");
  1277.  
  1278.     return this._prefs;
  1279.   },
  1280.  
  1281.   get extensions() {
  1282.     return this._extensions;
  1283.   },
  1284.  
  1285.   get events() {
  1286.     if (this._events == null)
  1287.         this._events = new Events();
  1288.  
  1289.     return this._events;
  1290.   },
  1291.  
  1292.   get bookmarks() {
  1293.     if (this._bookmarks == null)
  1294.       this._bookmarks = new BookmarkFolder(null, null);
  1295.  
  1296.     return this._bookmarks;
  1297.   },
  1298.  
  1299.   get windows() {
  1300.     var win = [];
  1301.     var enum = Utilities.windowMediator.getEnumerator("navigator:browser");
  1302.  
  1303.     while (enum.hasMoreElements())
  1304.       win.push(new Window(enum.getNext()));
  1305.  
  1306.     return win;
  1307.   },
  1308.  
  1309.   get activeWindow() {
  1310.     return new Window(Utilities.windowMediator.getMostRecentWindow("navigator:browser"));
  1311.   }
  1312. };
  1313.  
  1314. //module initialization
  1315. function NSGetModule(aCompMgr, aFileSpec) {
  1316.   return XPCOMUtils.generateModule([Application]);
  1317. }
  1318.